package edu.stanford.bmir.protege.web.server.stkosutil;

import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.EntityType;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.RemoveAxiom;

import edu.stanford.bmir.protege.web.client.rpc.data.EntityData;
import edu.stanford.bmir.protege.web.client.rpc.data.PaginationData;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.StkosUtilService;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.MetaItemList;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.SearchCondition;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.TripleData;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilEntityType;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CancelCutterResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.RemoveEntityResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.actions.CancelCutterAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.CancelEnricherResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.CandidateEntity;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnrichmentOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.EnrichmentResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.OriginEntity;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.SourceThesaurus;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.actions.CancelEnricherAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.actions.CreateCandidateClassesAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.actions.CreateCandidateClassesResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.actions.CreateTriplesAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.enricher.actions.CreateTriplesResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.CancelMapperResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.MappingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.MappingResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.mapper.actions.CancelMapperAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.merger.CancelMergerResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.merger.MergenceOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.merger.MergenceResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.merger.actions.CancelMergerAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.merger.actions.MergeEntityAction;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.merger.actions.MergeEntityResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.statistics.StatisticsResult;
import edu.stanford.bmir.protege.web.server.PaginationServerUtil;
import edu.stanford.bmir.protege.web.server.SessionConstants;
import edu.stanford.bmir.protege.web.server.WebProtegeRemoteServiceServlet;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.stkosutil.common.search.UtilSearcher;
import edu.stanford.bmir.protege.web.server.stkosutil.cutter.CutEntityRemover;
import edu.stanford.bmir.protege.web.server.stkosutil.cutter.OntologyCutter;
import edu.stanford.bmir.protege.web.server.stkosutil.cutter.handlers.CancelCutterActionHandler;
import edu.stanford.bmir.protege.web.server.stkosutil.enricher.OntologyEnricher;
import edu.stanford.bmir.protege.web.server.stkosutil.enricher.handlers.CancelEnricherActionHandler;
import edu.stanford.bmir.protege.web.server.stkosutil.enricher.handlers.CreateCandidateClassesActionHandler;
import edu.stanford.bmir.protege.web.server.stkosutil.enricher.handlers.CreateCandidateTriplesActionHandler;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OntologyMapper;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.handlers.CancelMapperActionHandler;
import edu.stanford.bmir.protege.web.server.stkosutil.merger.ClassMerger;
import edu.stanford.bmir.protege.web.server.stkosutil.merger.handlers.CancelMergerActionHandler;
import edu.stanford.bmir.protege.web.server.stkosutil.statistics.EntityCounter;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;

public class StkosUtilServiceImpl extends WebProtegeRemoteServiceServlet implements StkosUtilService{
	 public String addAnnotaionValue(String projectName, String entityName, String annotaionName, String annotaionValue){
		 UserId userId = this.getUserId();
		 OWLAPIProject project = this.getProject(projectName);
		 RenderingManager rm = project.getRenderingManager();
	     OWLClass owlClass = rm.getEntity(entityName, EntityType.CLASS);
	        
		 OWLDataFactory df = project.getDataFactory();
		 
		 OWLAnnotation commentAnno = df.getOWLAnnotation(
				 df.getRDFSComment(),
				 df.getOWLLiteral(annotaionValue, "en"));
		// Specify that the pizza class has an annotation
		 OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(
				 owlClass.getIRI(), commentAnno);
		 AddAxiom addAxiom =  new AddAxiom(project.getRootOntology(), ax);
		 List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
		 changes.add(addAxiom);
		 project.applyChanges(userId, changes, "scy test for adding annotaion");
		 return "success";
	 }
	 
	 public void updateAnnotaionValue(String projectName, String entityName, UtilEntityType entityType, String annotaionName, String annotaionValue){
		 List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
		
		 UserId userId = this.getUserId();
		 OWLAPIProject project = this.getProject(projectName);
		 RenderingManager rm = project.getRenderingManager();
	     OWLClass owlClass = rm.getEntity(entityName, EntityType.CLASS);
	     OWLDataFactory df = project.getDataFactory();
	     for (OWLAnnotation annotation : owlClass.getAnnotations(project.getRootOntology(), df.getRDFSComment())) {
	    		 if (annotation.getValue() instanceof OWLLiteral) {
		    		 OWLLiteral commentValue = (OWLLiteral) annotation.getValue();
		    		 String value = commentValue.getLiteral();
		    		  if (MetaItemList.annotationMatch(annotaionValue,value)){
		    			  OWLAxiom oldAx = df.getOWLAnnotationAssertionAxiom(
		    						 owlClass.getIRI(), annotation);
		    			  RemoveAxiom removeAxiom = new RemoveAxiom(project.getRootOntology(),oldAx);
		    			  changes.add(removeAxiom);
		    		  }
	    		 }//end if
	     }//end for
		 
		 OWLAnnotation commentAnno = df.getOWLAnnotation(
				 df.getRDFSComment(),
				 df.getOWLLiteral(annotaionValue, "en"));
		// Specify that the pizza class has an annotation
		 OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(
				 owlClass.getIRI(), commentAnno);
		 AddAxiom addAxiom =  new AddAxiom(project.getRootOntology(), ax);
		 
		 changes.add(addAxiom);
		 project.applyChanges(userId, changes, "scy test for adding annotaion");
		
	 }
	 
	 public Integer removeAnnotationValue(String projectName, String entityName, UtilEntityType entityType, String annotaionName, String annotationValuePattern){
		 int count = 0;
		 List<OWLOntologyChange> changes = new ArrayList<OWLOntologyChange>();
		 
		 UserId userId = this.getUserId();
		 OWLAPIProject project = this.getProject(projectName);
		 RenderingManager rm = project.getRenderingManager();
	     OWLClass owlClass = rm.getEntity(entityName, EntityType.CLASS);
	     OWLDataFactory df = project.getDataFactory();
	     for (OWLAnnotation annotation : owlClass.getAnnotations(project.getRootOntology(), df.getRDFSComment())) {
	    		 if (annotation.getValue() instanceof OWLLiteral) {
		    		 OWLLiteral commentValue = (OWLLiteral) annotation.getValue();
		    		 String value = commentValue.getLiteral();
		    		  if (MetaItemList.annotationMatch(annotationValuePattern, value)){
		    			  count++;
		    			  OWLAxiom oldAx = df.getOWLAnnotationAssertionAxiom(
		    						 owlClass.getIRI(), annotation);
		    			  RemoveAxiom removeAxiom = new RemoveAxiom(project.getRootOntology(),oldAx);
		    			  changes.add(removeAxiom);
		    		  }
	    		 }//end if
	     }//end for
		 
		 project.applyChanges(userId, changes, "removing annotaion " + annotationValuePattern);
		 return new Integer(count);
	 }
	 
	 public MappingResult mapOntologies(String project1Name, String project2Name, MappingOptions options){
		 OntologyMapper ontolgyMapper = new OntologyMapper();
		 return ontolgyMapper.mapOntologies(project1Name, project2Name, options, this.getUserId());
	 }
	 public UserId getUserInSession() {
	        HttpServletRequest request = getThreadLocalRequest();
	        final HttpSession session = request.getSession();
	        return SessionConstants.getUserId(session);
	  }
	  private UserId getUserId() {
	        return getUserInSession();
	  }
	  private OWLAPIProject getProject(String projectName) {
	        if (projectName == null) {
	            throw new NullPointerException("projectName must not be null");
	        }
	        ProjectId projectId = ProjectId.get(projectName);
	        // TODO: Log
	        return getProject(projectId);
	    }

	    /**
	     * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with the specified id exists then that project
	     * will be returned, otherwise, a fresh project will be created and that fresh project returned.
	     * @param projectId The id of the project.
	     * @return The OWL API project. Not <code>null</code>.
	     */
	    private OWLAPIProject getProject(ProjectId projectId) {
	        OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
	        return pm.getProject(projectId);
	    }

		
		public PaginationData<EntityData> search(String projectName,
				SearchCondition searchCondition, int start, int limit,
				String sort, String dir) {
			  List<EntityData> result = search(projectName, searchCondition);
		      return PaginationServerUtil.pagedRecords(result, start, limit, sort, dir);
		}

		public List<EntityData> search(String projectName,
				SearchCondition searchCondition) {
			UtilSearcher utilSearcher = new UtilSearcher();
			return utilSearcher.search(projectName, searchCondition);
		}

		@Override
		public CuttingResult cutOntology(String projectName,
				CuttingOptions options) {
			OntologyCutter cutter = new OntologyCutter();
			return cutter.cutOntology(projectName, options, this.getUserId());
		}

		@Override
		public List<CandidateEntity> fetchCandidateEntities(String projectName,
				OriginEntity originEntity, EnrichmentOptions options) {
			OntologyEnricher enricher = new OntologyEnricher();
			return enricher.fetchCandidateEntities(projectName, originEntity, options);
		}

		@Override
		public EnrichmentResult enrichOntology(String projectName,
				EnrichmentOptions options) {
			OntologyEnricher enricher = new OntologyEnricher();
			EnrichmentResult result = enricher.enrichOntology(projectName, options, this.getUserId());
			return result;
		}

		@Override
		public List<SourceThesaurus> fetchSourceThesaurusList() {
			OntologyEnricher enricher = new OntologyEnricher();
			return enricher.fetchSourceThesaurusList();
		}

		@Override
		public CreateCandidateClassesResult createCandidateClasses(
				String projectName, CreateCandidateClassesAction action) {
			CreateCandidateClassesActionHandler handler = new CreateCandidateClassesActionHandler();
			return handler.execute(projectName, action, this.getUserId());
			
		}

		
		public MergenceResult mergeOntology(String projectName,
				MergenceOptions options) {
			ClassMerger merger = new ClassMerger();
			String entityName = null;
			return merger.mergeOntology(projectName, entityName, options, this.getUserId());
		}

		@Override
		public MergeEntityResult mergeEntity(String projectName,
				MergeEntityAction action) {
			ClassMerger merger = new ClassMerger();
			String entityName = action.getEntityName();
			MergenceOptions options  = action.getOptions();
			merger.mergeOntology(projectName, entityName, options, this.getUserId());
			return merger.getMergeEntityResult(entityName);
		}

		@Override
		public RemoveEntityResult removeCutEntities(String projectName) {
			CutEntityRemover remover = new CutEntityRemover();
			return remover.removeEntities(projectName, this.getUserId());
					
		}

		@Override
		public List<TripleData> fetchCandidateTriples(String projectName,
				OriginEntity originEntity, EnrichmentOptions options) {
			OntologyEnricher enricher = new OntologyEnricher();
			try{
				return enricher.fetchCandidateTripleList(projectName, originEntity, options);
			}catch(Exception ex){
				ex.printStackTrace();
				return new ArrayList<TripleData>();
			}
		}

		@Override
		public CreateTriplesResult createTriples(String projectName,
				CreateTriplesAction action) {
			CreateCandidateTriplesActionHandler handler = new CreateCandidateTriplesActionHandler();
			return handler.execute(projectName, action, this.getUserId());
			
		}
		
		public StatisticsResult countEntities(String projectName){
			EntityCounter entityCounter = new EntityCounter();
			return entityCounter.countEntities(projectName);
		}

		
		public CancelMergerResult cancelMerger(String projectName, CancelMergerAction action){
			CancelMergerActionHandler handler = new CancelMergerActionHandler();
			return handler.execute(projectName, action, this.getUserId());
		}
		public CancelEnricherResult cancelEnricher(String projectName, CancelEnricherAction action){
			CancelEnricherActionHandler handler = new CancelEnricherActionHandler();
			return handler.execute(projectName, action, this.getUserId());
		}
		public CancelCutterResult cancelCutter(String projectName, CancelCutterAction action){
			CancelCutterActionHandler handler = new CancelCutterActionHandler();
			return handler.execute(projectName, action, this.getUserId());
		}
		public CancelMapperResult cancelMapper(String projectName, CancelMapperAction action){
			CancelMapperActionHandler handler = new CancelMapperActionHandler();
			return handler.execute(projectName, action, this.getUserId());
		}
}
